package cn.edu.sgu.www.mhxysy.restful.handler;

import cn.edu.sgu.www.mhxysy.exception.GlobalException;
import cn.edu.sgu.www.mhxysy.restful.JsonResult;
import cn.edu.sgu.www.mhxysy.restful.ResponseCode;
import cn.edu.sgu.www.mhxysy.util.HttpUtils;
import com.alibaba.fastjson.JSON;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import java.util.Objects;

/**
 * 全局异常处理类
 * @author 沐雨橙风ιε
 * @version 1.0
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理Exception
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult<Void> handleException(Exception e) {
        printMessage(e);

        return JsonResult.error(ResponseCode.SERVER_ERROR, e);
    }

    /**
     * 处理FeignException
     */
    @Deprecated
    @ExceptionHandler
    @SuppressWarnings("unchecked")
    public JsonResult<Void> handleException(FeignException e) {
        printMessage(e);

        // 获取异常信息
        String message = e.getLocalizedMessage();

        int start = message.indexOf("{");
        int end = message.indexOf("}");

        // 得到响应结果
        String result = message.substring(start, end + 1);

        // 转为JsonResult对象
        JsonResult<Void> jsonResult = JSON.parseObject(result, JsonResult.class);

        HttpServletResponse response = HttpUtils.getResponse();

        // 设置响应状态码
        response.setStatus(jsonResult.getCode());

        return jsonResult;
    }

    /**
     * 处理GlobalException
     */
    @ExceptionHandler
    public JsonResult<Void> handleException(GlobalException e) {
        HttpServletResponse response = HttpUtils.getResponse();

        response.setStatus(e.getResponseCode().getValue());

        return JsonResult.error(e.getResponseCode(), e);
    }

    /**
     * 处理NullPointerException
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public JsonResult<Void> handleException(NullPointerException e) {
        printMessage(e);

        return JsonResult.error(ResponseCode.SERVER_ERROR, "不规范的代码/请求导致了空指针异常。");
    }

    /**
     * 处理DuplicateKeyException
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.CONFLICT)
    public JsonResult<Void> handleException(DuplicateKeyException e) {
        printMessage(e);

        return JsonResult.error(ResponseCode.SERVER_ERROR, "点太快了，稍微休息一下吧~");
    }

    /**
     * 处理AuthenticationException
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public JsonResult<Void> handleException(AuthenticationException e) {
        printMessage(e);

        return JsonResult.error(ResponseCode.FORBIDDEN, e);
    }

    /**
     * 处理BindException：参数为实体类的方法上使用了@Validated注解
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public JsonResult<Void> handleException(BindException e) {
        printMessage(e);

        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        String defaultMessage = Objects.requireNonNull(fieldError).getDefaultMessage();

        return JsonResult.error(ResponseCode.BAD_REQUEST, defaultMessage);
    }

    /**
     * 处理ConstraintViolationException
     */
    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public JsonResult<Void> handleException(ConstraintViolationException e) {
        printMessage(e);

        String message = e.getMessage();
        int index = message.indexOf(":");

        return JsonResult.error(ResponseCode.BAD_REQUEST, message.substring(index + 2));
    }

    /**
     * 处理MethodArgumentNotValidException
     */
    @Deprecated
    @ExceptionHandler
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public JsonResult<Void> handleException(MethodArgumentNotValidException e) {
        printMessage(e);

        BindingResult bindingResult = e.getBindingResult();
        FieldError fieldError = bindingResult.getFieldError();
        String defaultMessage = Objects.requireNonNull(fieldError).getDefaultMessage();

        return JsonResult.error(ResponseCode.BAD_REQUEST, defaultMessage);
    }

    /**
     * 打印异常信息
     * @param e Exception
     */
    private void printMessage(Exception e) {
        String message = e.getMessage();

        log.error(message);

        e.printStackTrace();
    }

}